﻿/* 
 * Copyright 2010 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Proxy;
using NHibernate.Collection;
using System.Runtime.Serialization;
using System.Collections;
using System.Reflection;
using System.CodeDom;
using System.Collections.ObjectModel;
using Ngilead.Core;
using Ngilead.Core.Store.Stateless;
using Ngilead.Nhibernate;
using Ngilead.Core.Utils;

namespace Ngilead.WCF
{
    /// <summary>
    /// Data contract surrogate to serialize (Clone()) and deserialize (Merge()) objects
    /// </summary>
    public class NgileadDataContractSurrogate : IDataContractSurrogate
    {        
        /// <summary>
        /// Constructor
        /// </summary>
        public NgileadDataContractSurrogate()
        {
            PersistentBeanManager.Instance.ProxyStore = new StatelessProxyStore();
        }

        /// <summary>
        /// Get data contract type
        /// </summary>
        /// <param name="type">Source type</param>
        /// <returns>target type</returns>
        public Type GetDataContractType(Type type)
        {
            // AMOR : Code pour marquer les arrays de ILightEntity et les 
            // remplacer par des listes paramétrées avec le type de l'élément de l'array.
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>))
            {
                return typeof(List<>).MakeGenericType(type.GetGenericArguments()[0]);
            }
            return type;
        }

        /// <summary>
        /// Get object to serialize
        /// </summary>
        /// <param name="obj">Object</param>
        /// <param name="targetType">Target type</param>
        /// <returns>Object</returns>
        public object GetObjectToSerialize(object obj, Type targetType)
        {
            object result = null;
            if (obj != null && obj is WrapperWCF && ((WrapperWCF)obj).Action == WrapperWCF.enumTargetAction.Clone)
            {
                result = PersistentBeanManager.Instance.Clone(((WrapperWCF)obj).Value);
            }
            else
                result = obj;

            //if (result is Array)
            //{
            //    targetType = typeof(IList<>).MakeGenericType(result.GetType().GetElementType());
            //}

            return result;
        }

        /// <summary>
        /// Get deserialized object
        /// </summary>
        /// <param name="obj">Object</param>
        /// <param name="targetType">Target type</param>
        /// <returns>Object</returns>
        public object GetDeserializedObject(object obj, Type targetType)
        {
            object result = null;
            if (obj != null && obj is WrapperWCF && ((WrapperWCF)obj).Action == WrapperWCF.enumTargetAction.Merge)
            {
                result = PersistentBeanManager.Instance.Merge(((WrapperWCF)obj).Value);
            }
            else
                result = obj;

            //if (result is Array)
            //{
            //    targetType = typeof(IList<>).MakeGenericType(result.GetType().GetElementType());
            //}

            return result;
        }

        /// <summary>
        /// Get custom data to export
        /// </summary>
        /// <param name="memberInfo">MemberInfo</param>
        /// <param name="dataContractType">Type</param>
        /// <returns>Objet</returns>
        public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
        {
            return null;
        }

        /// <summary>
        /// Get custom data to export
        /// </summary>
        /// <param name="clrType">Type</param>
        /// <param name="dataContractType">Data contract type</param>
        /// <returns>Object</returns>
        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            return null;
        }

        /// <summary>
        /// Get known custom data types
        /// </summary>
        /// <param name="customDataTypes">Collection of Type</param>
        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
        }

        /// <summary>
        /// Get referenced type on import
        /// </summary>
        /// <param name="typeName">Type name</param>
        /// <param name="typeNamespace">Type namespace</param>
        /// <param name="customData">Custom data</param>
        /// <returns>Type</returns>
        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            return null;
        }

        /// <summary>
        /// Process imported type
        /// </summary>
        /// <param name="typeDeclaration">Type declaration</param>
        /// <param name="compileUnit">Compile unit</param>
        /// <returns>CodeTypeDeclaration</returns>
        public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
        {
            return typeDeclaration;
        }

        #region Internal methods

        /// <summary>
        /// Indicates if the argument object is a WrapperWCF and should be cloned or merged
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private bool IsWrapperWCF(Object obj)
        {
            if (obj != null && obj is WrapperWCF)
                return true;
            else
                return false;
        }        

        #endregion
    }

}
